#include "COperation.hpp"
#include "CPlayer.hpp"
#include "CField.hpp"

#include <cassert>
#include <memory>
#include <iostream>
#include <algorithm>
#include <functional>

////////////////////////////////////////////////////////////////
// IOperation::Params
//
/// Parameter of Operations
class Operations::IOperation::Param
{
	friend class RankedOperations;

public:
	std::vector<IOperation*> Ops;
	std::vector<int> Results;
	std::vector<CMob*> DeadMobs;
	CField& Field;

protected:
	Param(CField field)
		: Field(field)
	{}
};

////////////////////////////////////////////////////////////////
// RankedOperations::Impl
//
/// Implementation of RankedOperations
struct RankedOperations::Impl
	: private Operations::IOperation::Param
{
	friend class RankedOperations;

	typedef Operations::IOperation::Param base;
	typedef std::multimap<uint, Operations::IOperation*> multimap;
	
	multimap ranked_operations;

	/**
	* @brief Constructor
	*/
	Impl(
		CPlayers& players, CField& field)
		: base(field)
	{
		players.copy<COperation>(
			std::inserter(ranked_operations,
				ranked_operations.begin()),
			std::ptr_fun(&RankedOperations::Impl::makeIndex));
		players.foreach<COperations>(
			std::mem_fun(&COperations::clear));
	}
	
	bool executeNextOperations();
	int& operator()(uint num);
	void removeOperations(CMob* mob);
	static uint calcRank(Operations::IOperation* p);
	static multimap::value_type makeIndex(Operations::IOperation* p);
};

/**
* @brief Execute next operation.
*
* Operations that has same priority are proccessed at same time.
* Vetor of result is reused for next operations.
*
* @return State of the queue.
* @retval true Executed operations.
* @retval false Nothing to do.
*/
bool RankedOperations::Impl::executeNextOperations()
{
	// Remove and delete mob at the last time
	for(std::vector<CMob*>
		::iterator it = DeadMobs.begin();
		it != DeadMobs.end();
		++it)
	{
		removeOperations(*it);
		(*it)->remove();
	}
	
	// delete executed operation
	for(std::vector<Operations::IOperation*>
		::iterator it = Ops.begin();
		it != Ops.end();
		++it)
	{
		delete *it;
	}
	Ops.clear();

	// Nothing to be done for all
	if(ranked_operations.empty())
		return false;

	// move operations to temporary execution queue.
	// Those will be ordered by priority.
	// (std::map sorts elements by key automatically.)
	multimap::iterator it = ranked_operations.begin();
	uint current = (uint)-1;
	
	// add op if it has same key with first one
	while(it != ranked_operations.end()){
		if(current == (uint)-1) // first one
			current = it->first;
		else if(current != it->first) // key is unmatch
			break;
		
		Ops.push_back(it->second);
		ranked_operations.erase(it);
		++it;
	}

	// resize Results
	Results.resize(Ops.size());

	//execute first one
	Ops.at(0)->callExecuter(*this);

	return true;
}

/**
* @brief Accessor to Results
*
* @param num Id of result;
*
* @return Reference to the result
*/
int& RankedOperations::Impl::operator()(uint num)
{
	return Results[num];
}

/**
* @brief Remove operations that is had by removing mob.
*
* @param mob A pointer to CMob
*/
void RankedOperations::Impl::removeOperations(CMob* mob)
{
	for(multimap::iterator it = ranked_operations.begin();
		it != ranked_operations.end();
		++it)
	{
		if(it->second->pMob == mob)
			ranked_operations.erase(it);
	}
}

/**
* @brief Calculate priority
*
* priority = Mob Id + Operation Id * 100
* (Lower is execute earlier.)
*/
uint RankedOperations::Impl
	::calcRank(Operations::IOperation* p)
{
	return p->pMob->getType() + p->getId() * 100;
}

/**
* @brief Make index that is pair of priority and reference
*
* This function is usually called by std::transform().
*
* @param p A pointer of an operation.
*
* @return An element of multimap
*/
RankedOperations::Impl::multimap::value_type
	RankedOperations::Impl::makeIndex(Operations::IOperation* p)
{
	return std::make_pair(calcRank(p), p);
}

////////////////////////////////////////////////////////////////
// Operations
//
/// Operation classes
namespace Operations
{
	/**
	* @brief Interface of iterating (See CPlayers::foreach())
	*/
	IOperation::Container IOperation::getParent()
	{
		return &CMob::Ops;
	}

	/**
	* @brief Execute operation
	*
	* @param ops List of same operations
	*/
	void IOperation::Execute(Param& param)
	{
		for(uint i = 0;
			i < param.Ops.size();
			i++)
		{
			param.Results[i]
				= param.Ops[i]->process(param);
		}
	}
	
	/**
	* @fn IOperation::process()
	* @brief Process one operation
	*
	* It'll be called in IOperation::Execute() automatically;
	*
	* @return The result of operation
	*/

	/**
	* @fn IOperation::getId()
	* @brief Get operation's Id
	*
	* Id is also used as priority of execution.
	* Less is going to execute earlier.
	*
	* @return Id of operation.
	*/
	
	/**
	* @class IOperationProxy
	* @brief Proxy for IOperation
	*
	* This proxy class is for Execute() and Id().
	*
	* getId()やcallExecuter()はvirtualとして振舞わなければならず、実装はstaticでなければならないが、virtual staticな関数はC++の規約に違反するのでvirtualな関数からstaticな部分を分離しなければならない。通常は派生クラス全てにこの処理を書く必要があり冗長だが、このプロキシクラスはその部分をテンプレートを使うことにより統一している。
	*
	* @tparam T Class for call Execute()
	* @tparam Base The base class
	*/
	
	/**
	* @brief Bridge function for Id()
	*
	* @return Id
	*/
	template<class T, class Base>
	uint IOperationProxy<T, Base>::getId() const
	{
		return T::Id();
	}
	
	/**
	* @brief Bridge function at execution
	*
	* @param param parameter
	*/
	template<class T, class Base>
	void IOperationProxy<T, Base>::callExecuter(Param& param)
	{
		T::Execute(param);
	}

	/**
	* @brief Constructor
	*
	* @param t Point of object
	*/
	CMove::CMove(position_t t)
		: to(t)
	{
		to.z = 3;
	}

	/**
	* @copybrief IOperation::Execute(Param& param)
	*
	* - OK Successful
	* - COLLISION Collided another shi
	*/
	void CMove::Execute(Param& param)
	{
		std::vector<position_t> dest(param.Ops.size());

		// Copy destination and remove from previous place
		for(uint i = 0;
			i < param.Ops.size();
			i++)
		{
			// If Id is duplicated, it'll failure.
			CMove* p = dynamic_cast<CMove*>(param.Ops[i]);
			assert(p != NULL);
			dest[i] = p->to;
			param.Field.removeMob(
				p->pMob->getPos());
		}

		// Apply destination
		for(uint i = 0;
			i < dest.size();
			i++)
		{
			CMob* pMob = param.Field[dest[i]].pMob;
			CMove* p = reinterpret_cast<CMove*>(param.Ops[i]);
			if(pMob != NULL)
			{
				param.Field.removeMob(dest[i]);
				param.DeadMobs.push_back(pMob);
				param.DeadMobs.push_back(p->pMob);
				param.Results[i] = COLLISION;
			}else
			{
				// 同じMobが2度動こうとした時の対策。
				param.Field.removeMob(pMob->getPos());
				param.Field.putMob(*pMob, dest[i]);
				param.Results[i] = OK;
			}
		}
	}

	/// @copydoc IOperation::print()
	void CMove::print() const
	{
		std::cout << "Move: " << to << std::endl;
	}

	/**
	* @brief Constructor
	*
	* @param position Position to attack
	* @param damege Damege of attack
	*/
	CAttack::CAttack(position_t position, uint damage)
		: pos(position), dmg(damage)
	{
	}

	/**
	* @copybrief IOperation::Execute(Param& param)
	*
	* - OK Successful
	* - MISS Miss-hit
	*/
	void CAttack::Execute(Param& param)
	{
		// make damage
		std::vector<CMob*> dead_mobs;
		for(uint i = 0;
			i < param.Ops.size();
			i++)
		{
			CAttack* p = dynamic_cast<CAttack*>(param.Ops[i]);
			CMob* target = param.Field[p->pos].pMob;
			if(target == NULL)
			{
				param.Results[i] = MISS;
			}else
			{
				if(target->damage(p->dmg))
					dead_mobs.push_back(target);
				param.Results[i] = OK;
			}
		}
	}

	/// @copydoc IOperation::print()
	void CAttack::print() const
	{
		std::cout << "Attack:"
			<< pos << ": " << dmg << std::endl;
	}

	/// @copydoc CMove::Execute(Param& param)
	void CSurface::Execute(Param& param)
	{
		IOperation::Execute(param);
		CMove::Execute(param);
	}
	
	/// @copydoc IOperation::process()
	int CSurface::process(Param& param)
	{
		to = pMob->getPos();
		to.z++;
		assert(to.z < param.Field.getDepth());
		return 0;
	}

	/// @copydoc IOperation::print()
	void CSurface::print() const
	{
		std::cout << "Surface" << std::endl;
		static_cast<const CMove* const>(this)->print();
	}

	/// @copydoc CMove::Execute(Param& param)
	void CDive::Execute(Param& param)
	{
		IOperation::Execute(param); // call CDive::proccess
		CMove::Execute(param); // move mob using CMove
	}

	/// @copydoc IOperation::process()
	int CDive::process(Param& param)
	{
		to = pMob->getPos();
		assert(to.z > 0);
		to.z--;
		pMob->addOperation(new CDiveCounter(3));
		return 0;
	}

	/// @copydoc IOperation::print()
	void CDive::print() const
	{
		std::cout << "Dive" << std::endl;
		this->CMove::print();
	}

	
	/**
	* @brief Constructor
	*
	* @param count Count
	*/
	CDive::CDiveCounter::CDiveCounter(uint count)
		: c(count)
	{
	}

	/**
	* @copybrief IOperation::Execute(Param& param)
	*
	* - OK Successful
	* - ABORT Ship has already surfaced
	*/
	void CDive::CDiveCounter::Execute(Param& param)
	{
		IOperation::Execute(param); // call process()
		CMove::Execute(param);
	}
	
	/// @copydoc IOperation::process()
	int CDive::CDiveCounter::process(Param& param)
	{
		// if the mob has already been at surfece, destruct this.
		if(this->pMob->getPos().z
				>= param.Field.getDepth())
		{
			return ABORT;
		}

		to = pMob->getPos();
		if(c > 1)
		{
			// Decrement counter
			pMob->addOperation(new CDiveCounter(c - 1));
		}else
		{
			// Move up to surface
			to.z = param.Field.getDepth() - 1;
		}
		return OK;
	}
	
	void CDive::CDiveCounter::print() const
	{
		std::cout << "DivingCounter(" << c <<")" << std::endl;
	}
	
	/*
	/// @copydoc IOperation::print()
	void CSearch::print() const
	{
		std::cout << "Search" << std::endl;
	}*/

	/**
	* @class CSearch::CSearchConclude
	* @par Flow of operation
	* Execute operation -> Expand sight -> End of Turn ->
	* Giving Operation(Serching affect at this time) ->
	* (Execute operation) -> Reduce sight and delete
	*/
}

/**
* @brief Constructor
*
* @param players An instance of CPlayers
* @param instance An instance of CField
*/
RankedOperations::RankedOperations(
	CPlayers& players, CField& instance)
	: pImpl(new Impl(players, instance))
{
}

/// Destructor
RankedOperations::~RankedOperations()
{
	delete pImpl;
}

/// @copydoc RankedOperations::Impl::executeNextOperations()
bool RankedOperations::executeNextOperations()
{
	return pImpl->executeNextOperations();
}

/**
* @brief Copy operation and result
*
* @code
* result_info ri;
* ro.assignResults(ri);
* results[0].first	 <- IOperation* (Operation)
* results[0].second	 <- int (return code)
* @code
*
* @note ri is going to clear when it assigns.
*
* @param ri Out
*/
void RankedOperations::assignResults(result_info& ri) const
{
	ri.clear();
	std::transform(
		pImpl->Ops.begin(), pImpl->Ops.end(),
		pImpl->Results.begin(), std::back_inserter(ri),
		std::ptr_fun(&std::make_pair<Operations::IOperation*, int>)
		);
}

/// Print about executed operation with result.
void RankedOperations::print() const
{
	for(uint i = 0;
		i < pImpl->Results.size();
		i++)
	{
		pImpl->Ops.at(i)->print();
		std::cout << "result:" << pImpl->Results[i]
			<< std::endl << std::endl;
	}
}

